/** * Copyright 2009-2013 Oy Vaadin Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.vaadin.addon.jpacontainer.provider; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityNotFoundException; import javax.persistence.TransactionRequiredException; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Order; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import com.vaadin.addon.jpacontainer.EntityContainer; import com.vaadin.addon.jpacontainer.EntityManagerProvider; import com.vaadin.addon.jpacontainer.EntityProvider; import com.vaadin.addon.jpacontainer.LazyLoadingDelegate; import com.vaadin.addon.jpacontainer.QueryModifierDelegate; import com.vaadin.addon.jpacontainer.SortBy; import com.vaadin.addon.jpacontainer.filter.util.AdvancedFilterableSupport; import com.vaadin.addon.jpacontainer.filter.util.FilterConverter; import com.vaadin.addon.jpacontainer.metadata.EntityClassMetadata; import com.vaadin.addon.jpacontainer.metadata.MetadataFactory; import com.vaadin.addon.jpacontainer.metadata.PropertyKind; import com.vaadin.addon.jpacontainer.util.CollectionUtil; import com.vaadin.v7.data.Container.Filter; import com.vaadin.v7.data.util.filter.And; import com.vaadin.v7.data.util.filter.Compare.Equal; import com.vaadin.v7.data.util.filter.Compare.Greater; import com.vaadin.v7.data.util.filter.Compare.Less; import com.vaadin.v7.data.util.filter.Or; /** * A read-only entity provider that works with a local {@link EntityManager}. * Most important features and limitations: * <ul> * <li>Does not do any internal caching, all information is always accessed * directly from the EntityManager</li> * <li>Explicitly detaches entities by default (see * {@link #isEntitiesDetached() }) * <ul> * <li>Performs a serialize-deserialize cycle to clone entities in order to * explicitly detach them from the persistence context (<b>This is ugly!</b>).</li> * </ul> * </li> * <li>Uses lazy-loading of entities (when using detached entities, references * and collections within the entities should be configured to be fetched * eagerly, though)</li> * </ul> * * This entity provider does not perform very well, as every method call results * in at least one query being sent to the entity manager. If speed is desired, * {@link CachingLocalEntityProvider} should be used instead. However, this * entity provider consumes less memory than the caching provider. * * @author Petter Holmström (Vaadin Ltd) * @since 1.0 */ public class LocalEntityProvider<T> implements EntityProvider<T>, Serializable { private static final long serialVersionUID = 1601796410565144708L; private transient EntityManager entityManager; private EntityClassMetadata<T> entityClassMetadata; private boolean entitiesDetached = true; private EntityManagerProvider entityManagerProvider = null; /** * Creates a new <code>LocalEntityProvider</code>. * * @param entityClass * the entity class (must not be null). * @param entityManager * the entity manager to use (must not be null). */ public LocalEntityProvider(Class<T> entityClass, EntityManager entityManager) { this(entityClass); assert entityManager != null : "entityManager must not be null"; setEntityManager(entityManager); } /** * Creates a new <code>LocalEntityProvider</code>. The entity manager or an * entity manager provider must be set using * {@link #setEntityManager(javax.persistence.EntityManager)} or * {@link #setEntityManagerProvider(com.vaadin.addon.jpacontainer.EntityManagerProvider)} * respectively. * * @param entityClass * the entity class (must not be null). */ public LocalEntityProvider(Class<T> entityClass) { assert entityClass != null : "entityClass must not be null"; this.entityClassMetadata = MetadataFactory.getInstance() .getEntityClassMetadata(entityClass); } /** * Creates a new <code>LocalEntityProvider</code> with the specified * {@link EntityManagerProvider}. * * @param entityClass * @param entityManagerProvider */ public LocalEntityProvider(Class<T> entityClass, EntityManagerProvider entityManagerProvider) { this(entityClass); assert entityManagerProvider != null : "entityManagerProvider must not be null"; setEntityManagerProvider(entityManagerProvider); } private Serializable serializableEntityManager; private QueryModifierDelegate queryModifierDelegate; /** * The lazy loading delegate explicitly handles loading lazy collections * where needed (e.g. when using Hibernate) */ private LazyLoadingDelegate lazyLoadingDelegate; // TODO Test serialization of entity manager protected Object writeReplace() throws ObjectStreamException { if (entityManager != null && entityManager instanceof Serializable) { serializableEntityManager = (Serializable) entityManager; } return this; } protected Object readResolve() throws ObjectStreamException { if (serializableEntityManager != null) { this.entityManager = (EntityManager) serializableEntityManager; } return this; } /** * Sets the {@link EntityManagerProvider} that is used to find the current * entity manager unless set using * {@link #setEntityManager(javax.persistence.EntityManager)} * * @param entityManagerProvider * The entity manager provider to set. */ public void setEntityManagerProvider( EntityManagerProvider entityManagerProvider) { this.entityManagerProvider = entityManagerProvider; } /** * Gets the {@link EntityManagerProvider} that is used to find the current * entity manager unless one is specified using * {@link #setEntityManager(javax.persistence.EntityManager)}. * * @return the entity manager provider, */ public EntityManagerProvider getEntityManagerProvider() { return entityManagerProvider; } /** * Sets the entity manager. * * @param entityManager * the entity manager to set. */ public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } /** * Gets the metadata for the entity class. * * @return the metadata (never null). */ protected EntityClassMetadata<T> getEntityClassMetadata() { return this.entityClassMetadata; } /** * Gets the entity manager. If no entity manager has been set, the one * returned by the registered entity manager provider is returned. * * @return the entity manager. */ public EntityManager getEntityManager() { if (entityManager != null) { return entityManager; } return entityManagerProvider.getEntityManager(); } /** * Gets the entity manager. * * @return the entity manager (never null). * @throws IllegalStateException * if no entity manager is set. */ protected EntityManager doGetEntityManager() throws IllegalStateException { if (getEntityManager() == null) { throw new IllegalStateException("No entity manager specified"); } return getEntityManager(); } /** * Creates a copy of <code>original</code> and adds an entry for the primary * key to the end of the list. * * @param original * the original list of sorting instructions (must not be null, * but may be empty). * @return a new list with the added entry for the primary key. */ protected List<SortBy> addPrimaryKeyToSortList(List<SortBy> original) { if (sortByListContainsPrimaryKey(original)) { return original; } ArrayList<SortBy> newList = new ArrayList<SortBy>(); newList.addAll(original); if (getEntityClassMetadata().hasEmbeddedIdentifier()) { for (String p : getEntityClassMetadata().getIdentifierProperty() .getTypeMetadata().getPersistentPropertyNames()) { newList.add(new SortBy(getEntityClassMetadata() .getIdentifierProperty().getName() + "." + p, true)); } } else { newList.add(new SortBy(getEntityClassMetadata() .getIdentifierProperty().getName(), true)); } return Collections.unmodifiableList(newList); } /** * @param original * @return */ private boolean sortByListContainsPrimaryKey(List<SortBy> original) { for (SortBy sb : original) { EntityClassMetadata<T> metadata = getEntityClassMetadata(); if (metadata.hasEmbeddedIdentifier()) { if (sb.getPropertyId() .equals(metadata.getIdentifierProperty() .getTypeMetadata().getPersistentPropertyNames() .iterator().next())) { return true; } } else { if (sb.getPropertyId().equals( metadata.getIdentifierProperty().getName())) { return true; } } } return false; } /** * Translates SortBy instances, which possibly contain nested properties * (e.g. name.firstName, name.lastName) into Order instances which can be * used in a CriteriaQuery. * * @param sortBy * the SortBy instance to translate * @param swapSortOrder * swaps the specified sort order if true. * @param cb * the {@link CriteriaBuilder} to use * @param root * the {@link CriteriaQuery} {@link Root} to be used. * @return */ protected Order translateSortBy(SortBy sortBy, boolean swapSortOrder, CriteriaBuilder cb, Root<T> root) { String sortedPropId = sortBy.getPropertyId().toString(); // First split the id and build a Path. String[] idStrings = sortedPropId.split("\\."); Path<T> path = null; if (idStrings.length > 1 && !isEmbedded(idStrings[0])) { // This is a nested property, we need to LEFT JOIN path = root.join(idStrings[0], JoinType.LEFT); for (int i = 1; i < idStrings.length; i++) { if (i < idStrings.length - 1) { path = ((Join<?, ?>) path) .join(idStrings[i], JoinType.LEFT); } else { path = path.get(idStrings[i]); } } } else { // non-nested or embedded, we can select as usual path = AdvancedFilterableSupport.getPropertyPathTyped(root, sortedPropId); } // Make and return the Order instances. if (sortBy.isAscending() != swapSortOrder) { return cb.asc(path); } else { return cb.desc(path); } } /** * @param propertyId * @return */ private boolean isEmbedded(String propertyId) { return entityClassMetadata.getProperty(propertyId).getPropertyKind() == PropertyKind.EMBEDDED; } /** * Creates a filtered query that does not do any sorting. * * @see #createFilteredQuery(com.vaadin.addon.jpacontainer.EntityContainer, * java.util.List, com.vaadin.data.Container.Filter, java.util.List, * boolean) * @param fieldsToSelect * the fields to select (must not be null). * @param filter * the filter to apply, or null if no filters should be applied. * @return the query (never null). */ protected TypedQuery<Object> createUnsortedFilteredQuery( EntityContainer<T> container, List<String> fieldsToSelect, Filter filter) { return createFilteredQuery(container, fieldsToSelect, filter, null, false); } /** * Creates a filtered, optionally sorted, query. * * @param fieldsToSelect * the fields to select (must not be null). * @param filter * the filter to apply, or null if no filters should be applied. * @param sortBy * the fields to sort by (must include at least one field), or * null if the result should not be sorted at all. * @param swapSortOrder * true to swap the sort order, false to use the sort order * specified in <code>sortBy</code>. Only applies if * <code>sortBy</code> is not null. * @return the query (never null). */ protected TypedQuery<Object> createFilteredQuery( EntityContainer<T> container, List<String> fieldsToSelect, Filter filter, List<SortBy> sortBy, boolean swapSortOrder) { assert fieldsToSelect != null : "fieldsToSelect must not be null"; assert sortBy == null || !sortBy.isEmpty() : "sortBy must be either null or non-empty"; CriteriaBuilder cb = doGetEntityManager().getCriteriaBuilder(); CriteriaQuery<Object> query = cb.createQuery(); Root<T> root = query.from(entityClassMetadata.getMappedClass()); tellDelegateQueryWillBeBuilt(container, cb, query); List<Predicate> predicates = new ArrayList<Predicate>(); if (filter != null) { predicates.add(FilterConverter.convertFilter(filter, cb, root)); } tellDelegateFiltersWillBeAdded(container, cb, query, predicates); if (!predicates.isEmpty()) { query.where(CollectionUtil.toArray(Predicate.class, predicates)); } tellDelegateFiltersWereAdded(container, cb, query); List<Order> orderBy = new ArrayList<Order>(); if (sortBy != null && sortBy.size() > 0) { for (SortBy sortedProperty : sortBy) { orderBy.add(translateSortBy(sortedProperty, swapSortOrder, cb, root)); } } tellDelegateOrderByWillBeAdded(container, cb, query, orderBy); query.orderBy(orderBy); tellDelegateOrderByWereAdded(container, cb, query); if (fieldsToSelect.size() > 1 || getEntityClassMetadata().hasEmbeddedIdentifier()) { List<Path<?>> paths = new ArrayList<Path<?>>(); for (String fieldPath : fieldsToSelect) { paths.add(AdvancedFilterableSupport.getPropertyPathTyped(root, fieldPath)); } query.multiselect(paths.toArray(new Path<?>[paths.size()])); } else { query.select(AdvancedFilterableSupport.getPropertyPathTyped(root, fieldsToSelect.get(0))); } tellDelegateQueryHasBeenBuilt(container, cb, query); return doGetEntityManager().createQuery(query); } protected boolean doContainsEntity(EntityContainer<T> container, Object entityId, Filter filter) { assert entityId != null : "entityId must not be null"; String entityIdPropertyName = getEntityClassMetadata() .getIdentifierProperty().getName(); CriteriaBuilder cb = doGetEntityManager().getCriteriaBuilder(); CriteriaQuery<Long> query = cb.createQuery(Long.class); Root<T> root = query.from(getEntityClassMetadata().getMappedClass()); tellDelegateQueryWillBeBuilt(container, cb, query); List<Predicate> predicates = new ArrayList<Predicate>(); predicates.add(cb.equal(root.get(entityIdPropertyName), cb.literal(entityId))); if (filter != null) { predicates.add(FilterConverter.convertFilter(filter, cb, root)); } tellDelegateFiltersWillBeAdded(container, cb, query, predicates); if (!predicates.isEmpty()) { query.where(CollectionUtil.toArray(Predicate.class, predicates)); } tellDelegateFiltersWereAdded(container, cb, query); if (getEntityClassMetadata().hasEmbeddedIdentifier()) { /* * Hibernate will generate SQL for "count(obj)" that does not run on * HSQLDB. "count(*)" works fine, but then EclipseLink won't work. * With this hack, this method should work with both Hibernate and * EclipseLink. */ query.select(cb.count(root.get(entityIdPropertyName).get( getEntityClassMetadata().getIdentifierProperty() .getTypeMetadata().getPersistentPropertyNames() .iterator().next()))); } else { query.select(cb.count(root.get(entityIdPropertyName))); } tellDelegateQueryHasBeenBuilt(container, cb, query); TypedQuery<Long> tq = doGetEntityManager().createQuery(query); return tq.getSingleResult() == 1; } public boolean containsEntity(EntityContainer<T> container, Object entityId, Filter filter) { return doContainsEntity(container, entityId, filter); } protected T doGetEntity(Object entityId) { assert entityId != null : "entityId must not be null"; T entity = doGetEntityManager().find( getEntityClassMetadata().getMappedClass(), entityId); return detachEntity(entity); } public T getEntity(EntityContainer<T> container, Object entityId) { return doGetEntity(entityId); } protected Object doGetEntityIdentifierAt(EntityContainer<T> container, Filter filter, List<SortBy> sortBy, int index) { if (sortBy == null) { sortBy = Collections.emptyList(); } TypedQuery<Object> query = createFilteredQuery(container, Arrays.asList(getEntityClassMetadata().getIdentifierProperty() .getName()), filter, addPrimaryKeyToSortList(sortBy), false); query.setMaxResults(1); query.setFirstResult(index); List<?> result = query.getResultList(); if (result.isEmpty()) { return null; } else { return result.get(0); } } public Object getEntityIdentifierAt(EntityContainer<T> container, Filter filter, List<SortBy> sortBy, int index) { return doGetEntityIdentifierAt(container, filter, sortBy, index); } protected int doGetEntityCount(EntityContainer<T> container, Filter filter) { String entityIdPropertyName = getEntityClassMetadata() .getIdentifierProperty().getName(); CriteriaBuilder cb = doGetEntityManager().getCriteriaBuilder(); CriteriaQuery<Long> query = cb.createQuery(Long.class); Root<T> root = query.from(getEntityClassMetadata().getMappedClass()); tellDelegateQueryWillBeBuilt(container, cb, query); List<Predicate> predicates = new ArrayList<Predicate>(); if (filter != null) { predicates.add(FilterConverter.convertFilter(filter, cb, root)); } tellDelegateFiltersWillBeAdded(container, cb, query, predicates); if (!predicates.isEmpty()) { query.where(CollectionUtil.toArray(Predicate.class, predicates)); } tellDelegateFiltersWereAdded(container, cb, query); if (getEntityClassMetadata().hasEmbeddedIdentifier()) { /* * Hibernate will generate SQL for "count(obj)" that does not run on * HSQLDB. "count(*)" works fine, but then EclipseLink won't work. * With this hack, this method should work with both Hibernate and * EclipseLink. */ query.select(cb.count(root.get(entityIdPropertyName).get( getEntityClassMetadata().getIdentifierProperty() .getTypeMetadata().getPersistentPropertyNames() .iterator().next()))); } else { query.select(cb.count(root.get(entityIdPropertyName))); } tellDelegateQueryHasBeenBuilt(container, cb, query); TypedQuery<Long> tq = doGetEntityManager().createQuery(query); return tq.getSingleResult().intValue(); } public int getEntityCount(EntityContainer<T> container, Filter filter) { return doGetEntityCount(container, filter); } protected Object doGetFirstEntityIdentifier(EntityContainer<T> container, Filter filter, List<SortBy> sortBy) { if (sortBy == null) { sortBy = Collections.emptyList(); } List<String> keyFields = Arrays.asList(getEntityClassMetadata() .getIdentifierProperty().getName()); // if (getEntityClassMetadata().hasEmbeddedIdentifier()) { // keyFields = new ArrayList<String>(); // for (String p : getEntityClassMetadata().getIdentifierProperty() // .getTypeMetadata().getPersistentPropertyNames()) { // keyFields.add(getEntityClassMetadata().getIdentifierProperty() // .getName() + "." + p); // } // } TypedQuery<Object> query = createFilteredQuery(container, keyFields, filter, addPrimaryKeyToSortList(sortBy), false); query.setMaxResults(1); List<?> result = query.getResultList(); if (result.isEmpty()) { return null; } else { return result.get(0); } } public Object getFirstEntityIdentifier(EntityContainer<T> container, Filter filter, List<SortBy> sortBy) { return doGetFirstEntityIdentifier(container, filter, sortBy); } protected Object doGetLastEntityIdentifier(EntityContainer<T> container, Filter filter, List<SortBy> sortBy) { if (sortBy == null) { sortBy = Collections.emptyList(); } // The last 'true' parameter switches the sort order -> the last row is // the first result. TypedQuery<Object> query = createFilteredQuery(container, Arrays.asList(getEntityClassMetadata().getIdentifierProperty() .getName()), filter, addPrimaryKeyToSortList(sortBy), true); query.setMaxResults(1); List<?> result = query.getResultList(); if (result.isEmpty()) { return null; } else { return result.get(0); } } public Object getLastEntityIdentifier(EntityContainer<T> container, Filter filter, List<SortBy> sortBy) { return doGetLastEntityIdentifier(container, filter, sortBy); } /** * If <code>backwards</code> is false, this method will return the * identifier of the entity next to the entity identified by * <code>entityId</code>. If true, this method will return the identifier of * the entity previous to the entity identified by <code>entityId</code>. * <code>filter</code> and <code>sortBy</code> is used to define and limit * the list of entities to be used for determining the sibling. * * @param entityId * the identifier of the entity whose sibling to retrieve (must * not be null). * @param filter * an optional filter to limit the entities (may be null). * @param sortBy * the order in which the list should be sorted (must not be * null). * @param backwards * true to fetch the previous sibling, false to fetch the next * sibling. * @return the identifier of the "sibling". */ protected Object getSibling(EntityContainer<T> container, Object entityId, Filter filter, List<SortBy> sortBy, boolean backwards) { TypedQuery<Object> query = createSiblingQuery(container, entityId, filter, sortBy, backwards); query.setMaxResults(1); List<?> result = query.getResultList(); if (result.size() != 1) { return null; } else { return result.get(0); } } /** * This method creates a query that can be used to fetch the siblings of a * specific entity. If <code>backwards</code> is false, the query will begin * with the entity next to the entity identified by <code>entityId</code>. * If <code>backwards</code> is false, the query will begin with the entity * prior to the entity identified by <code>entityId</code>. * * @param entityId * the identifier of the entity whose sibling to retrieve (must * not be null). * @param filter * an optional filter to limit the entities (may be null). * @param sortBy * the order in which the list should be sorted (must not be * null). * @param backwards * true to fetch the previous sibling, false to fetch the next * sibling. * @return the query that will return the sibling and all the subsequent * entities unless limited. */ protected TypedQuery<Object> createSiblingQuery( EntityContainer<T> container, Object entityId, Filter filter, List<SortBy> sortBy, boolean backwards) { assert entityId != null : "entityId must not be null"; assert sortBy != null : "sortBy must not be null"; Filter limitingFilter; sortBy = addPrimaryKeyToSortList(sortBy); if (sortBy.size() == 1) { // The list is sorted by primary key if (backwards) { limitingFilter = new Less(getEntityClassMetadata() .getIdentifierProperty().getName(), entityId); } else { limitingFilter = new Greater(getEntityClassMetadata() .getIdentifierProperty().getName(), entityId); } } else { // We have to fetch the values of the sorted fields T currentEntity = getEntity(container, entityId); if (currentEntity == null) { throw new EntityNotFoundException( "No entity found with the ID " + entityId); } // Collect the values into a map for easy access Map<Object, Object> filterValues = new HashMap<Object, Object>(); for (SortBy sb : sortBy) { filterValues.put( sb.getPropertyId(), getEntityClassMetadata().getPropertyValue( currentEntity, sb.getPropertyId().toString())); } // Now we can build a filter that limits the query to the entities // below entityId List<Filter> orFilters = new ArrayList<Filter>(); for (int i = sortBy.size() - 1; i >= 0; i--) { // TODO Document this code snippet once it works // TODO What happens with null values? List<Filter> caseFilters = new ArrayList<Filter>(); SortBy sb; for (int j = 0; j < i; j++) { sb = sortBy.get(j); caseFilters.add(new Equal(sb.getPropertyId(), filterValues .get(sb.getPropertyId()))); } sb = sortBy.get(i); if (sb.isAscending() ^ backwards) { caseFilters.add(new Greater(sb.getPropertyId(), filterValues.get(sb.getPropertyId()))); } else { caseFilters.add(new Less(sb.getPropertyId(), filterValues .get(sb.getPropertyId()))); } orFilters.add(new And(CollectionUtil.toArray(Filter.class, caseFilters))); } limitingFilter = new Or(CollectionUtil.toArray(Filter.class, orFilters)); } // Now, we can create the query Filter queryFilter; if (filter == null) { queryFilter = limitingFilter; } else { queryFilter = new And(filter, limitingFilter); } TypedQuery<Object> query = createFilteredQuery(container, Arrays.asList(getEntityClassMetadata().getIdentifierProperty() .getName()), queryFilter, sortBy, backwards); return query; } protected Object doGetNextEntityIdentifier(EntityContainer<T> container, Object entityId, Filter filter, List<SortBy> sortBy) { if (sortBy == null) { sortBy = Collections.emptyList(); } return getSibling(container, entityId, filter, sortBy, false); } public Object getNextEntityIdentifier(EntityContainer<T> container, Object entityId, Filter filter, List<SortBy> sortBy) { return doGetNextEntityIdentifier(container, entityId, filter, sortBy); } protected Object doGetPreviousEntityIdentifier( EntityContainer<T> container, Object entityId, Filter filter, List<SortBy> sortBy) { if (sortBy == null) { sortBy = Collections.emptyList(); } return getSibling(container, entityId, filter, sortBy, true); } public Object getPreviousEntityIdentifier(EntityContainer<T> container, Object entityId, Filter filter, List<SortBy> sortBy) { return doGetPreviousEntityIdentifier(container, entityId, filter, sortBy); } /** * Detaches <code>entity</code> from the entity manager. If * <code>entity</code> is null, then null is returned. If * {@link #isEntitiesDetached() } is false, <code>entity</code> is returned * directly. * * @param entity * the entity to detach. * @return the detached entity. */ protected T detachEntity(T entity) { if (entity == null) { return null; } if (isEntitiesDetached()) { getEntityManager().detach(entity); } return entity; } public boolean isEntitiesDetached() { return entitiesDetached; } public void setEntitiesDetached(boolean detached) throws UnsupportedOperationException { this.entitiesDetached = detached; } protected List<Object> doGetAllEntityIdentifiers( EntityContainer<T> container, Filter filter, List<SortBy> sortBy) { if (sortBy == null) { sortBy = Collections.emptyList(); } sortBy = addPrimaryKeyToSortList(sortBy); TypedQuery<Object> query = createFilteredQuery(container, Arrays.asList(getEntityClassMetadata().getIdentifierProperty() .getName()), filter, sortBy, false); return Collections.unmodifiableList(query.getResultList()); } public List<Object> getAllEntityIdentifiers(EntityContainer<T> container, Filter filter, List<SortBy> sortBy) { return doGetAllEntityIdentifiers(container, filter, sortBy); } /* * (non-Javadoc) * * @see * com.vaadin.addon.jpacontainer.EntityProvider#setQueryModifierDelegate * (com.vaadin.addon.jpacontainer.EntityProvider.QueryModifierDelegate) */ public void setQueryModifierDelegate(QueryModifierDelegate delegate) { this.queryModifierDelegate = delegate; } /* * (non-Javadoc) * * @see * com.vaadin.addon.jpacontainer.EntityProvider#getQueryModifierDelegate() */ public QueryModifierDelegate getQueryModifierDelegate() { return queryModifierDelegate; } // QueryModifierDelegate helper methods private void tellDelegateQueryWillBeBuilt(EntityContainer<T> container, CriteriaBuilder cb, CriteriaQuery<?> query) { if (queryModifierDelegate != null) { queryModifierDelegate.queryWillBeBuilt(cb, query); } else if (container.getQueryModifierDelegate() != null) { container.getQueryModifierDelegate().queryWillBeBuilt(cb, query); } } private void tellDelegateQueryHasBeenBuilt(EntityContainer<T> container, CriteriaBuilder cb, CriteriaQuery<?> query) { if (queryModifierDelegate != null) { queryModifierDelegate.queryHasBeenBuilt(cb, query); } else if (container.getQueryModifierDelegate() != null) { container.getQueryModifierDelegate().queryHasBeenBuilt(cb, query); } } private void tellDelegateFiltersWillBeAdded(EntityContainer<T> container, CriteriaBuilder cb, CriteriaQuery<?> query, List<Predicate> predicates) { if (queryModifierDelegate != null) { queryModifierDelegate.filtersWillBeAdded(cb, query, predicates); } else if (container.getQueryModifierDelegate() != null) { container.getQueryModifierDelegate().filtersWillBeAdded(cb, query, predicates); } } private void tellDelegateFiltersWereAdded(EntityContainer<T> container, CriteriaBuilder cb, CriteriaQuery<?> query) { if (queryModifierDelegate != null) { queryModifierDelegate.filtersWereAdded(cb, query); } else if (container.getQueryModifierDelegate() != null) { container.getQueryModifierDelegate().filtersWereAdded(cb, query); } } private void tellDelegateOrderByWillBeAdded(EntityContainer<T> container, CriteriaBuilder cb, CriteriaQuery<?> query, List<Order> orderBy) { if (queryModifierDelegate != null) { queryModifierDelegate.orderByWillBeAdded(cb, query, orderBy); } else if (container.getQueryModifierDelegate() != null) { container.getQueryModifierDelegate().orderByWillBeAdded(cb, query, orderBy); } } private void tellDelegateOrderByWereAdded(EntityContainer<T> container, CriteriaBuilder cb, CriteriaQuery<?> query) { if (queryModifierDelegate != null) { queryModifierDelegate.orderByWasAdded(cb, query); } else if (container.getQueryModifierDelegate() != null) { container.getQueryModifierDelegate().orderByWasAdded(cb, query); } } public Object getIdentifier(T entity) { return entityClassMetadata.getPropertyValue(entity, entityClassMetadata .getIdentifierProperty().getName()); } public T refreshEntity(T entity) { if (getEntityManager().contains(entity)) { try { getEntityManager().refresh(entity); } catch (IllegalArgumentException e) { // detached, removed or something, get by id from em and refresh // than non-detached object entity = findAndRefresh(entity); } catch (EntityNotFoundException e) { return null; } catch (TransactionRequiredException e) { // TODO: handle exception, only in transactional? } } else { entity = findAndRefresh(entity); } return entity; } private T findAndRefresh(T entity) { entity = getEntityManager().find( getEntityClassMetadata().getMappedClass(), getIdentifier(entity)); if (entity != null) { try { // now try to refresh the attached entity getEntityManager().refresh(entity); entity = detachEntity(entity); } catch (TransactionRequiredException e) { // NOP } catch (Exception e) { throw new RuntimeException(e); } } return entity; } /* * (non-Javadoc) * * @see * com.vaadin.addon.jpacontainer.EntityContainer#setLazyLoadingDelegate( * com.vaadin.addon.jpacontainer.EntityContainer.LazyLoadingDelegate) */ public void setLazyLoadingDelegate(LazyLoadingDelegate delegate) { lazyLoadingDelegate = delegate; if (lazyLoadingDelegate != null) { lazyLoadingDelegate.setEntityProvider(this); } } public LazyLoadingDelegate getLazyLoadingDelegate() { return lazyLoadingDelegate; } /* * (non-Javadoc) * * @see com.vaadin.addon.jpacontainer.EntityProvider#refresh() */ public void refresh() { // Nothing to do in this implementation, since we don't keep any // items/entities cached. } }